﻿using System;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Demo.AttachFileService.Mongo.Domain.Entities;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace Demo.AttachFileService.Mongo.Comon {

    public class MongoDbQuery<TEntity> where TEntity : class, IEntity<string> {
        private readonly IMongoDatabaseProvider _databaseProvider;

        public virtual IMongoDatabase Database {
            get { return _databaseProvider.Database; }
        }

        public virtual IMongoCollection<TEntity> Collection {
            get {
                return _databaseProvider.Database.GetCollection<TEntity>(typeof(TEntity).Name);
            }
        }

        public MongoDbQuery(IMongoDatabaseProvider mongoDatabaseProvider) {
            _databaseProvider = mongoDatabaseProvider;
        }

        #region Count
        /// <summary>
        ///     获取记录数
        /// </summary>
        /// <returns></returns>
        public long Count() {
            return Count(new BsonDocument());
        }

        /// <summary>
        ///     获取记录数
        /// </summary>
        /// <returns></returns>
        public async Task<long> CountAsync() {
            return await CountAsync(new BsonDocument());
        }
        /// <summary>
        ///     获取记录数
        /// </summary>
        /// <typeparam name="TEntity">数据库实体类型</typeparam>
        /// <param name="expression">筛选条件</param>
        /// <returns></returns>
        public long Count(Expression<Func<TEntity, bool>> expression) {
            return Collection.CountDocuments(expression);
        }

        /// <summary>
        ///     获取记录数
        /// </summary>
        /// <typeparam name="TEntity">数据库实体类型</typeparam>
        /// <param name="filter">过滤器</param>
        /// <returns></returns>
        public long Count(FilterDefinition<TEntity> filter) {
            return Collection.CountDocuments(filter);
        }

        /// <summary>
        ///     异步获取记录数
        /// </summary>
        /// <typeparam name="TEntity">数据库实体类型</typeparam>
        /// <param name="expression">筛选条件</param>
        /// <returns></returns>
        public async Task<long> CountAsync(Expression<Func<TEntity, bool>> expression) {
            return await Collection.CountDocumentsAsync(expression);
        }

        /// <summary>
        ///     异步获取记录数
        /// </summary>
        /// <typeparam name="TEntity">数据库实体类型</typeparam>
        /// <param name="filter">过滤器</param>
        /// <returns></returns>
        public async Task<long> CountAsync(FilterDefinition<TEntity> filter) {
            return await Collection.CountDocumentsAsync(filter);
        }
        #endregion

        #region Get

        /// <summary>
        ///     获取单个对象
        /// </summary>
        /// <param name="filter">过滤器</param>
        /// <returns></returns>
        public async Task<TEntity> GetAsync(FilterDefinition<TEntity> filter) {
            return await Collection.Find(filter).FirstOrDefaultAsync();
        }

        /// <summary>
        ///     获取单个对象
        /// </summary>
        /// <param name="filter">过滤器</param>
        /// <returns></returns>
        public IEntity Get(FilterDefinition<TEntity> filter) {
            return Collection.Find(filter).FirstOrDefault();
        }

        public async Task<TEntity> GetAsync(string id) {
            return await GetAsync(new BsonDocument("_id", id));
        }
        public IEntity Get(string id) {
            return Get(new BsonDocument("_id", id));
        }

        /// <summary>
        ///     获取单个对象
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <returns></returns>
        public TEntity Get(Expression<Func<TEntity, bool>> expression) {
            return Collection.Find(expression).FirstOrDefault();
        }

        /// <summary>
        ///     异步获取单个对象
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <returns></returns>
        public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> expression) {
            return await Collection.Find(expression).FirstOrDefaultAsync();
        }

        /// <summary>
        ///     获取单个对象
        /// </summary>
        /// <typeparam name="TResult">新实体类型</typeparam>
        /// <param name="filter">过滤器</param>
        /// <param name="projection">新实体映射</param>
        /// <returns></returns>
        public TResult Get<TResult>(FilterDefinition<TEntity> filter,
            Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(filter).Project(projection).FirstOrDefault();
        }

        /// <summary>
        ///     异步获取单个对象
        /// </summary>
        /// <typeparam name="TResult">新实体类型</typeparam>
        /// <param name="filter">过滤器</param>
        /// <param name="projection">新实体映射</param>
        /// <returns></returns>
        public async Task<TResult> GetAsync<TResult>(FilterDefinition<TEntity> filter,
            Expression<Func<TEntity, TResult>> projection) {

            return await Collection.Find(filter).Project(projection).FirstOrDefaultAsync();
        }


        /// <summary>
        ///     获取单个对象
        /// </summary>
        /// <typeparam name="TResult">新实体类型</typeparam>
        /// <param name="expression">筛选条件</param>
        /// <param name="projection">新实体映射</param>
        /// <returns></returns>
        public TResult Get<TResult>(Expression<Func<TEntity, bool>> expression,
            Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(expression).Project(projection).FirstOrDefault();
        }



        /// <summary>
        ///     异步获取单个对象
        /// </summary>
        /// <typeparam name="TResult">新实体类型</typeparam>
        /// <param name="expression">筛选条件</param>
        /// <param name="projection">新实体映射</param>
        /// <returns></returns>
        public async Task<TResult> GetAsync<TResult>(Expression<Func<TEntity, bool>> expression,
            Expression<Func<TEntity, TResult>> projection) {

            return await Collection.Find(expression).Project(projection).FirstOrDefaultAsync();
        }
        #endregion

        #region GetAll
        /// <summary>
        ///     获取集合
        /// </summary>
        /// <returns></returns>
        public List<TEntity> GetAll() {
            return Collection.Find(new BsonDocument()).ToList();
        }

        /// <summary>
        ///     异步获取集合
        /// </summary>
        /// <returns></returns>
        public async Task<List<TEntity>> GetAllAsync() {
            return await Collection.Find(new BsonDocument()).ToListAsync();
        }
        #endregion

        #region Find
        public async Task<List<TEntity>> FindAsync(FilterDefinition<TEntity> filter) {
            return await Collection.Find(filter).ToListAsync();
        }
        public async Task<List<TEntity>> FindAsync(Expression<Func<TEntity, bool>> expression) {
            return await Collection.Find(expression).ToListAsync();
        }
        public async Task<List<TResult>> FindAsync<TResult>(FilterDefinition<TEntity> filter,
           Expression<Func<TEntity, TResult>> projection) {
            return await Collection.Find(filter).Project(projection).ToListAsync();
        }
        public async Task<List<TResult>> FindAsync<TResult>(
            Expression<Func<TEntity, bool>> expression,
            Expression<Func<TEntity, TResult>> projection) {
            return await Collection.Find(expression).Project(projection).ToListAsync();
        }

        public List<TEntity> Find(FilterDefinition<TEntity> filter) {
            return Collection.Find(filter).ToList();
        }

        public List<TEntity> Find(Expression<Func<TEntity, bool>> expression) {
            return Collection.Find(expression).ToList();
        }

        public List<TResult> Find<TResult>(Expression<Func<TEntity, bool>> expression,
            Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(expression).Project(projection).ToList();
        }

        public List<TResult> Find<TResult>(FilterDefinition<TEntity> filter,
            Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(filter).Project(projection).ToList();
        }
        #endregion

        #region PageList
        public List<TEntity> PageList(int pageIndex, int pageSize) {
            return Collection.Find(new BsonDocument())
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToList();
        }

        public List<TResult> PageList<TResult>(int pageIndex, int pageSize,
            Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(new BsonDocument())
                        .Project(projection)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToList();
        }

        public List<TEntity> PageList(int pageIndex, int pageSize, FilterDefinition<TEntity> filter,
            SortDefinition<TEntity> sort) {
            return Collection.Find(filter)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToList();
        }

        public List<TResult> PageList<TResult>(int pageIndex, int pageSize,
            FilterDefinition<TEntity> filter, SortDefinition<TEntity> sort, Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(filter)
                        .Project(projection)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToList();
        }

        public List<TEntity> PageList(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> where,
            SortDefinition<TEntity> sort) {
            return Collection.Find(where)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToList();
        }

        public List<TResult> PageList<TResult>(int pageIndex, int pageSize,
            Expression<Func<TEntity, bool>> where, SortDefinition<TEntity> sort, Expression<Func<TEntity, TResult>> projection) {
            return Collection.Find(where)
                        .Project(projection)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToList();
        }
        #endregion

        #region PageListAsync
        public async Task<List<TEntity>> PageListAsync(int pageIndex, int pageSize) {
            return await Collection.Find(new BsonDocument())
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToListAsync();
        }

        public async Task<List<TResult>> PageListAsync<TResult>(int pageIndex, int pageSize,
            Expression<Func<TEntity, TResult>> projection) {
            return await Collection.Find(new BsonDocument())
                        .Project(projection)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToListAsync();
        }

        public async Task<List<TEntity>> PageListAsync(int pageIndex, int pageSize, FilterDefinition<TEntity> filter,
            SortDefinition<TEntity> sort) {
            return await Collection.Find(filter)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToListAsync();
        }

        public async Task<List<TResult>> PageListAsync<TResult>(int pageIndex, int pageSize,
            FilterDefinition<TEntity> filter, SortDefinition<TEntity> sort, Expression<Func<TEntity, TResult>> projection) {
            return await Collection.Find(filter)
                        .Project(projection)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToListAsync();
        }

        public async Task<List<TEntity>> PageListAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> where,
            SortDefinition<TEntity> sort) {
            return await Collection.Find(where)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToListAsync();
        }

        public async Task<List<TResult>> PageListAsync<TResult>(int pageIndex, int pageSize,
            Expression<Func<TEntity, bool>> where, SortDefinition<TEntity> sort, Expression<Func<TEntity, TResult>> projection) {
            return await Collection.Find(where)
                        .Project(projection)
                        .Sort(sort)
                        .Skip((pageIndex - 1) * pageSize)
                        .Limit(pageSize)
                        .ToListAsync();
        }
        #endregion
    }
}